﻿Imports System.Collections.Generic
Imports System.Configuration.Provider
Imports System.IO
Imports System.Linq
Imports TallComponents.Web.Pdf
Imports TallComponents.Web.Storage
Imports DatabaseStorageDemoVB.DatabaseStorageDemo.Model

Namespace DatabaseStorageDemo.Storage
   ''' <summary>
   ''' Storage provider stores files in a database.
   ''' </summary>
   Public Class DatabaseStorageProvider
      Inherits PdfStorageProvider
      ''' <summary>
      ''' Initializes the provider.
      ''' </summary>
      ''' <param name="name">The friendly name of the provider.</param>
      ''' <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
      ''' <exception cref="T:System.ArgumentNullException">
      ''' The name of the provider is null.
      ''' </exception>
      ''' <exception cref="T:System.ArgumentException">
      ''' The name of the provider has a length of zero.
      ''' </exception>
      ''' <exception cref="T:System.InvalidOperationException">
      ''' An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"/> on a provider after the provider has already been initialized.
      ''' </exception>
      Public Overrides Sub Initialize(name As String, config As System.Collections.Specialized.NameValueCollection)
         MyBase.Initialize(name, config)

         If String.IsNullOrEmpty(config("connectionString")) Then
            Throw New ProviderException("Missing required attribute 'connectionString' on DatabaseStorageProvider.")
         End If

         _connectionString = System.Configuration.ConfigurationManager.ConnectionStrings(config("connectionString")).ConnectionString

         Using context = New FileStorageDataContext(_connectionString)
            If Not context.DatabaseExists() Then
               Try
                  context.CreateDatabase()
               Catch ex As Exception
                  Throw New ProviderException("Unable to create database. Please review the connection string and make sure it's valid for your system." & vbLf & "The default configuration will try to connect to SqlServer Express and create the demonstration database.", ex)
               End Try
            End If
         End Using
      End Sub

      ''' <summary>
      ''' Gets information about a PDF document.
      ''' </summary>
      ''' <param name="key">The storage key.</param>
      ''' <returns>
      ''' A <see cref="Document"/> object describing the document with the specified <paramref name="key"/> 
      ''' or <c>null</c> if the key is not valid or associated with an invalid document.
      ''' </returns>
      Public Overrides Function GetDocument(key As String) As Document
         Dim result As Document = Nothing
         Dim id As Integer
         If Int32.TryParse(key, id) Then
            Using context = New FileStorageDataContext(_connectionString)
               Dim query = From file In context.Files Where file.FileId = id
                 Select New Document(New MemoryStream(file.Data), file.FileName, file.FileId.ToString())

               result = query.FirstOrDefault()
            End Using
         End If

         Return result
      End Function

      ''' <summary>
      ''' Stores a file.
      ''' </summary>
      ''' <param name="fileName">Name of the file.</param>
      ''' <param name="data">A seekable stream containing the file data.</param>
      ''' <returns>The unique key for the file.</returns>
      Public Overrides Function StoreFile(fileName As String, data As Stream) As String
         If data Is Nothing OrElse data.Length = 0 Then
            Throw New ArgumentException("Stream contains no data", "data")
         End If

         Using insertContext = New FileStorageDataContext(_connectionString)
            Dim newFile = New DatabaseFile() With { _
             .FileName = fileName, _
             .Data = New Byte(data.Length - 1) {}, _
             .LastModified = DateTime.Now _
            }

            If data.CanSeek Then
               data.Seek(0, SeekOrigin.Begin)
            End If
            data.Read(newFile.Data, 0, CInt(data.Length))

            Try
               ' don't know the key yet 
               Dim document = New Document(New MemoryStream(newFile.Data), fileName, String.Empty)
               newFile.Author = document.DocumentInfo.Author
               newFile.Creator = document.DocumentInfo.Creator
               newFile.Keywords = document.DocumentInfo.Keywords
               newFile.PageCount = document.Pages.Count
               newFile.Subject = document.DocumentInfo.Subject
               newFile.Title = document.DocumentInfo.Title
            Catch ex As Exception
               Throw New InvalidOperationException("The uploaded document does not appear to be valid", ex)
            End Try

            insertContext.Files.InsertOnSubmit(newFile)

            insertContext.SubmitChanges()

            Return newFile.FileId.ToString()
         End Using
      End Function

      ''' <summary>
      ''' Enumerates the documents in this storage provider.
      ''' </summary>
      ''' <returns>
      ''' Document information for each document in storage.
      ''' </returns>
      Public Function ListDocuments() As IEnumerable(Of DatabaseDocumentInfo)
         Dim datacontext = New FileStorageDataContext(_connectionString)
         Dim query = From file In datacontext.GetTable(Of DatabaseFile)() Order By file.FileName
			            Select New DatabaseDocumentInfo( If ( file.Title, file.FileName), file.Subject, file.Keywords, file.Author, file.Creator, file.FileId, _
				file.PageCount, file.FileName, file.LastModified)

         Return query
      End Function

      ''' <summary>
      ''' Gets information on all the documents in storage.
      ''' </summary>
      ''' <returns>
      ''' A list of <see cref="Document"/> objects, describing all PDF documents managed by this provider.
      ''' </returns>
      Public Overrides Function GetDocuments() As IList(Of Document)
         Dim datacontext = New FileStorageDataContext(_connectionString)
         Dim query = From file In datacontext.GetTable(Of DatabaseFile)() Order By file.FileName
               Select New Document(New MemoryStream(file.Data), file.FileName, file.FileId.ToString())

         Return query.ToList()
      End Function

      Private _connectionString As String
   End Class
End Namespace
